import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author wonghiu
 * @Date 2021/8/15 13:52
 * @Version 1.0.1
 * 线程同步
 */

public class ThreadPrint {
    public static void main(String[] args) {
//        new ThreadPrint().new Solution().printMethodNotify();
//        new ThreadPrint().new Solution().printMethodPark();
//        new ThreadPrint().new Solution().crossPrintNotify();
        new ThreadPrint().new Solution().crossPrintLock();
    }

    class Solution {
        Object object = new Object();
        volatile boolean flag = false;

        public void printMethodNotify() {
            new Thread(() -> {
                synchronized (object) {
                    System.out.println("a");
                    flag = true;
                    object.notifyAll();
                }
            }, "thread A").start();

            new Thread(() -> {
                synchronized (object) {
                    while (flag == false) {
                        try {
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("b");
                }
            }, "thread B").start();
        }

        public void printMethodPark() {
            Thread threadB = new Thread(() -> {
                LockSupport.park();
                System.out.println("b");
            }, "thread B");

            Thread threadA = new Thread(() -> {
                System.out.println("a");
                LockSupport.unpark(threadB);
            }, "thread A");
            threadA.start();
            threadB.start();
        }

        public void crossPrintNotify() {
            class CrossPrintObject {
                volatile private int nowIndex;
                volatile private int loopCount;

                public CrossPrintObject(int nowIndex, int loopCount) {
                    this.nowIndex = nowIndex;
                    this.loopCount = loopCount;
                }

                public void print(int curIndex, int nextIndex, String content) throws InterruptedException {
                    for (int i = 0; i < loopCount; i++) {
                        synchronized (this) {
                            while (nowIndex != curIndex) {
                                this.wait();
                            }
                            System.out.println(content);
                            nowIndex = nextIndex;
                            this.notifyAll();
                        }
                    }
                }
            }
            CrossPrintObject crossPrintObject = new CrossPrintObject(1, 3);
            new Thread(() -> {
                try {
                    crossPrintObject.print(1, 2, "1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "A").start();
            new Thread(() -> {
                try {
                    crossPrintObject.print(2, 3, "2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "B").start();
            new Thread(() -> {
                try {
                    crossPrintObject.print(3, 1, "3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "C").start();
        }

        public void crossPrintPark() {
            class CrossPrintObject {
                volatile private Thread nowThread;
                volatile private int loopCount;

                public CrossPrintObject(Thread nowThread, int loopCount) {
                    this.nowThread = nowThread;
                    this.loopCount = loopCount;
                }

                public void print(Thread nextThread, String content) {
                    for (int i = 0; i < loopCount; i++) {
                        LockSupport.park();
                        System.out.println(content);
                        LockSupport.unpark(nextThread);
                    }
                }
            }
        }

        public void crossPrintLock() {
            class CrossPrintObject extends ReentrantLock {

                volatile private int loopCount;
                private Map<Integer, Condition> map;

                public CrossPrintObject(int loopCount) {
                    this.loopCount = loopCount;
                    map = new HashMap<>();
                    Condition condition1 = this.newCondition();
                    Condition condition2 = this.newCondition();
                    Condition condition3 = this.newCondition();
                    map.put(1, condition1);
                    map.put(2, condition2);
                    map.put(3, condition3);
                }

                public void print(Integer curIndex, Integer nextIndex, String content) throws InterruptedException {
                    for (int i = 0; i < loopCount; i++) {
                        this.lock();
                        map.get(curIndex).await();
                        System.out.println(content);
                        map.get(nextIndex).signalAll();
                        this.unlock();
                    }
                }
            }
            CrossPrintObject crossPrintObject = new CrossPrintObject(3);
            new Thread(() -> {
                try {
                    crossPrintObject.print(1, 2, "1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "A").start();
            new Thread(() -> {
                try {
                    crossPrintObject.print(2, 3, "2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "B").start();
            new Thread(() -> {
                try {
                    crossPrintObject.print(3, 1, "3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "C").start();
            crossPrintObject.lock();
            crossPrintObject.map.get(1).signal();
            crossPrintObject.unlock();
        }
    }
}
